package com.jfinal.spring;

import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;

import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import com.jfinal.kit.PathKit;
import com.jfinal.util.ReflectUtils;
import com.jfinal.util.SpringUtils;

/**
 * spring 必要的 filter
 * @ClassName: AbstractSpringFilter 
 * @author huangx
 * @date 2015年2月4日 下午12:04:41
 */
public abstract class AbstractSpringJFinal {
	// spring location param
	public static final String CONTEXT_CONFIG_LOCATION_PARAM = "contextConfigLocation";
	public static final String CONFIGURATIONS_PARAM = "configurations";
	public static final String ANNOTATED_CLASSES_PARAM = "annotatedClasses";
	public static final String BASE_PACKAGES_PARAM = "basePackages";

	private ApplicationContext context;

	/**
	 * @Title: 初始化 spring 上下文
	 * @param filterConfig
	 */
	public void initApplicationContext(FilterConfig filterConfig) {
		// 优先 加载 ContextLoaderListener
		try {
			ReflectUtils.forName("org.springframework.web.context.ContextLoaderListener");
			context = org.springframework.web.context.support.WebApplicationContextUtils.getWebApplicationContext(filterConfig.getServletContext());
		} catch (Exception e) {}// ignore
		// init-param
		// 1. xml(contextConfigLocations)
		String contextConfigLocationsParam = filterConfig.getInitParameter(CONTEXT_CONFIG_LOCATION_PARAM);
		// 2. xml(configurations)
		String configurationsParam = filterConfig.getInitParameter(CONFIGURATIONS_PARAM);
		// 3. annotation(annotatedClasses)
		String annotatedClassesParam = filterConfig.getInitParameter(ANNOTATED_CLASSES_PARAM);
		// 4. annotation(basePackages)
		String basePackagesParam = filterConfig.getInitParameter(BASE_PACKAGES_PARAM);
		loadApplicationContext(contextConfigLocationsParam, configurationsParam, annotatedClassesParam, basePackagesParam);
		// 初始化完成跳出
		if (null != context) return;

		// context-param
		ServletContext sc = filterConfig.getServletContext();
		// 1. xml(contextConfigLocations)
		contextConfigLocationsParam = sc.getInitParameter(CONTEXT_CONFIG_LOCATION_PARAM);
		// 2. xml(configurations)
		configurationsParam = sc.getInitParameter(CONFIGURATIONS_PARAM);
		// 3. annotation(annotatedClasses)
		annotatedClassesParam = sc.getInitParameter(ANNOTATED_CLASSES_PARAM);
		// 4. annotation(basePackages)
		basePackagesParam = sc.getInitParameter(BASE_PACKAGES_PARAM);
		loadApplicationContext(contextConfigLocationsParam, configurationsParam, annotatedClassesParam, basePackagesParam);
		// 断言初始化成功
		Assert.notNull(context);
	}

	/**
	 * @Title: register JFinal
	 * @param configClass
	 * @return
	 */
	public Object registerJFinalBean(String configClass) {
		if (!context.containsBean(configClass)) {
			// 如果 spring 容器已存在则不注册
			// configClass 本身是个 className
			BeanDefinitionBuilder builder = SpringUtils.genericBean(configClass);
			AbstractBeanDefinition beanDefinition = builder.getRawBeanDefinition();
			beanDefinition.setScope(SpringUtils.SCOPE_SINGLETON);
			SpringUtils.registerBean(context, configClass, beanDefinition);
		}
		return context.getBean(configClass);
	}

	/**
	 * @Title: 获得 ApplicationContext  
	 * @return ApplicationContext 
	 * @throws
	 */
	public ApplicationContext getApplicationContext() {
		return context;
	}

	/**
	 * @Title: loadApplicationContext  
	 * @param contextConfigLocationsParam
	 * @param configurationsParam
	 * @param annotatedClassesParam
	 * @param basePackagesParam
	 * @return ApplicationContext 
	 * @throws
	 */
	protected void loadApplicationContext(String contextConfigLocationsParam, String configurationsParam, String annotatedClassesParam, String basePackagesParam) {
		if (context != null) return;
		if (!StringUtils.isEmpty(contextConfigLocationsParam)) loadApplicationContextByContextConfigLocation(contextConfigLocationsParam);
		else if (!StringUtils.isEmpty(configurationsParam)) loadApplicationContextByConfigurations(configurationsParam);
		else if (!StringUtils.isEmpty(annotatedClassesParam)) loadApplicationContextByAnnotatedClasses(annotatedClassesParam);
		else if (!StringUtils.isEmpty(basePackagesParam)) loadApplicationContextByBasePackages(basePackagesParam);
	}

	/*----------------- loadApplication ----------------------*/
	protected void loadApplicationContextByContextConfigLocation(String contextConfigLocationsParam) {
		context = SpringUtils.newClassPathXmlApplicationContextByConfigLocations(contextConfigLocationsParam.split(","));
	}

	protected void loadApplicationContextByBasePackages(String basePackagesParam) {
		context = SpringUtils.newAnnotationConfigApplicationContextByBasePackages(basePackagesParam.split(","));
	}

	protected void loadApplicationContextByAnnotatedClasses(String annotatedClassesParam) {
		context = SpringUtils.newAnnotationConfigApplicationContextByAnnotatedClasses(annotatedClassesParam.split(","));
	}

	/**
	 * @Title: 创建 ApplicationContext
	 * @param configurationsParam
	 * @return ApplicationContext
	 */
	protected void loadApplicationContextByConfigurations(String configurationsParam) {
		String[] configurations = configurationsParam.split(",");
		String webRootPath = PathKit.getWebRootPath() + "/";
		for (int i = 0; i < configurations.length; i++) {
			configurations[i] = webRootPath + configurations[i];
		}
		context = SpringUtils.newFileSystemXmlApplicationContextByConfigurations(configurations);
	}
}
